#include "stdafx.h"
#include "CollisionManager.h"
#include "Entity.h"
#include "N64.h"
#include "BasicTower.h"
#include "Enemy.h"
#include "Wall.h"
#include "TargetMediator.h"

CollisionManager::CollisionManager(void) : m_quad_tree(true, nullptr), m_collision_method(ECollisionMethod::COLL_QUAD_TREE) {
	XZ center;
	XZ half_dimension(8, 8);
	m_collision_boundary.Set(center, half_dimension);
	m_quad_tree.Set(AABB(XZ(46.5f, 45.6f), XZ(100.0f, 100.0f)));
	m_quad_tree.Subdivide(2);
	m_quad_tree.SetCollisionManager(this);
}
CollisionManager::~CollisionManager(void){ 
	ClearColliders(); 
	m_collision_callbacks.clear();
}

void CollisionManager::RemoveMe(Collider* p_collider){
	for (auto it = m_colliders.begin(); it != m_colliders.end(); it++){
		if (p_collider == (*it)){
			m_colliders.erase(it);
			p_collider->GetXZBoundingBox()->owner->RemoveBoundingBox(p_collider->GetXZBoundingBox());
			delete p_collider;
			return;
		}
	}
}

void CollisionManager::Update(){
	switch(m_collision_method){
	case ECollisionMethod::COLL_QUAD_TREE:
		{
			m_quad_tree.Update();
			m_quad_tree.CheckCollision();
			/*for (unsigned int i = 0; i < m_colliders.size(); i++){
				m_collision_boundary.center.x = m_colliders[i]->GetX();
				m_collision_boundary.center.z = m_colliders[i]->GetZ();
				std::vector<XZBoundingBox*> collision_list;
				m_quad_tree.QueryRange(collision_list, m_collision_boundary);
				if (!collision_list.empty()){
					for (unsigned int j = 0; j < collision_list.size(); j++){
						if (m_colliders[i] != collision_list[j]->user_data){
							Collider* collider = static_cast<Collider*>(collision_list[j]->user_data);
							if (m_colliders[i]->Intersects(collider)){
								auto it = m_collision_callbacks.find(std::make_pair(m_colliders[i]->GetUserData()->GetEntityType(), collider->GetUserData()->GetEntityType()));
								if (it != m_collision_callbacks.end()){
									(this->*it->second)(m_colliders[i]->GetUserData(), collider->GetUserData());
								}
							}
						}
					}
				}
			}*/
		}
		break;
	case ECollisionMethod::COLL_BRUTE_FORCE:
		{
			for (unsigned int i = 0; i < m_colliders.size(); i++) {
				for (unsigned int j = 0; j < m_colliders.size(); j++){
					if (m_colliders[i] != m_colliders[j]) {
						if (m_colliders[i]->Intersects(m_colliders[j])){
							auto it = m_collision_callbacks.find(std::make_pair(m_colliders[i]->GetUserData()->GetEntityType(), m_colliders[j]->GetUserData()->GetEntityType()));
							if (it != m_collision_callbacks.end()){
								(this->*it->second)(m_colliders[i]->GetUserData(), m_colliders[j]->GetUserData());
							}
						}
					}
				}
			}
		}
		break;
	default:
		break;
	}
}

void CollisionManager::Init(){
	m_collision_callbacks[MakeIntPair(Basic_Tower, N64_Logo)] = &CollisionManager::BasicTowerN64;
	m_collision_callbacks[MakeIntPair(N64_Logo, Basic_Tower)] = &CollisionManager::N64BasicTower;
	m_collision_callbacks[MakeIntPair(Enemy_Type, Basic_Tower)] = &CollisionManager::EnemyScan;
	m_collision_callbacks[MakeIntPair(Basic_Tower, Enemy_Type)] = &CollisionManager::ScanEnemy;
	m_collision_callbacks[MakeIntPair(Enemy_Type, Slow_Tower)] = &CollisionManager::EnemyScan;
	m_collision_callbacks[MakeIntPair(Slow_Tower, Enemy_Type)] = &CollisionManager::ScanEnemy;
	m_collision_callbacks[MakeIntPair(Projectile_Type, Enemy_Type)] = &CollisionManager::ProjectileEnemy;
	m_collision_callbacks[MakeIntPair(Enemy_Type, Projectile_Type)] = &CollisionManager::EnemyProjectile;
	m_collision_callbacks[MakeIntPair(Enemy_Projectile, Wall_Type)] = &CollisionManager::EnemyprojectileWall;
	m_collision_callbacks[MakeIntPair(Wall_Type, Enemy_Projectile)] = &CollisionManager::WallEnemyprojectile;
	m_collision_callbacks[MakeIntPair(Enemy_Type, Slow_Projectile)] = &CollisionManager::EnemySlowprojectile;
	m_collision_callbacks[MakeIntPair(Slow_Projectile, Enemy_Type)] = &CollisionManager::SlowprojectileEnemy;
}

std::pair<int, int> CollisionManager::MakeIntPair(int lhs, int rhs){
	return std::pair<int, int>(lhs, rhs);
}

void CollisionManager::AddCollider(Collider* p_collider) { 
	m_colliders.push_back(p_collider);
	switch (p_collider->GetType()){
	case EColliderType::COLLIDER_BOX:
		{
			BoxCollider* collider = static_cast<BoxCollider*>(p_collider);
			XZBoundingBox* xz_bounding_box = new XZBoundingBox(XZ(collider->GetBoundingBox().Center.x, collider->GetBoundingBox().Center.z), 
				XZ(collider->GetBoundingBox().Extents.x, collider->GetBoundingBox().Extents.z), (void*)p_collider);
			p_collider->SetXZBoundingBox(xz_bounding_box);
			m_quad_tree.Insert(xz_bounding_box);
		}
		break;
	case EColliderType::COLLIDER_SPHERE:
		{
			SphereCollider* collider = static_cast<SphereCollider*>(p_collider);
			float radius = collider->GetBoundingSphere().Radius;
			XZBoundingBox* xz_bounding_box = new XZBoundingBox(XZ(collider->GetBoundingSphere().Center.x, collider->GetBoundingSphere().Center.z),
				XZ(radius, radius), (void*)p_collider);
			p_collider->SetXZBoundingBox(xz_bounding_box);
			m_quad_tree.Insert(xz_bounding_box);
		}
		break;
	default:
		break;
	}
}

void CollisionManager::N64BasicTower(Entity* n64, Entity* basic_tower){

}

void CollisionManager::ProjectileEnemy(Entity* projectile, Entity* enemy){
	projectile->RemoveMe();
	static_cast<Enemy*>(enemy)->ModifyHealth(-1);
}

void CollisionManager::EnemyScan(Entity* p_enemy, Entity* p_basic_tower){
	Enemy* enemy = static_cast<Enemy*>(p_enemy);
	BasicTower* tower = static_cast<BasicTower*>(p_basic_tower);
	Entity* target = enemy->GetTargetMediator()->GetTarget(tower);

	if (target == nullptr){
		enemy->GetTargetMediator()->AddTarget(enemy, tower);
	}
}

void CollisionManager::WallEnemyprojectile(Entity* wall, Entity* enemy_projectile){
	enemy_projectile->RemoveMe();
	static_cast<Wall*>(wall)->ModifyHealth(-1);
}

void CollisionManager::EnemySlowprojectile(Entity* enemy, Entity* slow_projectile){
	slow_projectile->RemoveMe();
	static_cast<Enemy*>(enemy)->SetStatus(Enemy::EStatus::Slow);
}

inline void CollisionManager::ClearColliders(){
	if (!m_colliders.empty()){
		for (auto it = m_colliders.begin(); it != m_colliders.end(); it++){
			delete *it;
			*it = nullptr;
		}
		m_colliders.clear();
	}
}

void CollisionManager::Shut(){
	for (auto it = m_collision_callbacks.begin(); it != m_collision_callbacks.end(); it++){
		it->second = nullptr;
	}
}

void CollisionManager::CheckCollision(Entity* lhs, Entity* rhs){
	auto it = m_collision_callbacks.find(std::make_pair(lhs->GetEntityType(), rhs->GetEntityType()));
	if (it != m_collision_callbacks.end()){
		(this->*it->second)(lhs, rhs);
	}
}